﻿package com.zouloux.graphics 
{
	import flash.display.DisplayObject;
	import flash.display.InteractiveObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.utils.setTimeout;
	
	/**
	 * ...
	 * @author ...
	 */
	public class LineTracer extends Sprite
	{
		// La liste des lignes
		protected static var __instances:Array = [];
		
		/**
		 * Démarrer le lineTracer
		 * @param	pThickness : largeur du point, en pixel
		 * @param	pColor : couleur du point, en hexa
		 * @param	pList : la liste de points
		 * @return
		 */
		public static function create (pList:Array = null):LineTracer
		{
			// Créer une instance
			var instance:LineTracer = new LineTracer();
			
			// Ajouter plein de points
			instance.addPoints(pList);
			
			// Démarrer
			instance.start();
			
			// Retourner l'instance
			return instance;
		}
		
		/**
		 * Effacer toutes les lignes
		 */
		public static function clearAll ():void
		{
			for each (var line:LineTracer in __instances) 
			{
				line.kill();
			}
		}
		
		/**
		 * La liste des points à suivre
		 */
		private var _points				:Array			= [];
		
		/**
		 * La couleur du trait
		 */
		private var _color				:uint			= 0xFF0000;
		
		/**
		 * La largeur du trait
		 */
		private var _thickness			:Number			= 1;
		
		/**
		 * La vitesse
		 */
		private var _speed				:Number			= 5;
		
		/**
		 * L'index du point séléctionné
		 */
		private var _selectedPointIndex	:uint;
		
		
		/**
		 * Le point courant pour l'animation
		 */
		private var _currentPoint		:Point;
		
		/**
		 * Le point suivant
		 */
		private var _nextPoint			:Point;
		
		/**
		 * Les fonctions de fin
		 */
		private var _endFunctions		:Array			= [];
		
		/**
		 * Le mode de trait
		 */
		private var _sparkleMode		:Boolean		= false;
		
		/**
		 * Le sprite
		 */
		private var _sparkleSprite		:Sprite;
		
		
		/**
		 * La position actuelle
		 */
		private var _currentPosition	:Number			= 0;
		
		/**
		 * Le délais
		 */
		private var _delay				:Number			= 0;
		
		
		/**
		 * La liste des points
		 */
		public function get points():Array { return _points; }
		
		/**
		 * La couleur du trait
		 */
		public function get color():uint { return _color; }
		
		/**
		 * L'épaisseur du trait
		 */
		public function get thickness():Number { return _thickness; }
		
		/**
		 * La vitesse
		 */
		public function get speed():Number { return _speed; }
		
		/**
		 * Définir le sprite sparkle
		 */
		public function get sparkleSprite():Sprite { return _sparkleSprite; }
		
		
		
		// Les setters
		public function set points(value:Array):void 
		{
			_points = value;
		}
		public function set color(value:uint):void 
		{
			// Enregistrer la valeur
			_color = value;
			
			// Actualiser
			graphics.lineStyle(_thickness, _color);
		}
		public function set thickness(value:Number):void 
		{
			// Enregistrer la valeur
			_thickness = value;
			
			// Actualiser
			graphics.lineStyle(_thickness, _color);
		}
		public function set speed(value:Number):void 
		{
			_speed = value;
		}
		public function set sparkleSprite(value:Sprite):void 
		{
			_sparkleSprite = value;
		}
		
		
		// Les setters chainés
		public function setPoints(value:Array):LineTracer 
		{
			_points = value;
			
			// Retourner this
			return this;
		}
		public function setColor(value:uint):LineTracer
		{
			// Enregistrer la valeur
			_color = value;
			
			// Actualiser
			graphics.lineStyle(_thickness, _color);
			
			// Retourner this
			return this;
		}
		public function setThickness(value:Number):LineTracer 
		{
			// Enregistrer la valeur
			_thickness = value;
			
			// Actualiser
			graphics.lineStyle(_thickness, _color);
			
			// Retourner this
			return this;
		}
		public function setSpeed(value:Number):LineTracer 
		{
			_speed = value;
			
			// Retourner this
			return this;
		}
		public function setSparkleSprite(value:Sprite):LineTracer 
		{
			// Effacer l'ancien
			if (_sparkleSprite != null)
				removeChild(_sparkleSprite);
			
			// Enregistrer le nouveau
			_sparkleSprite = value;
			
			// Vérifier s'il y a bien un nouveau, si oui l'ajouter
			if (value != null)
				addChild(_sparkleSprite);
			
			// Retourner this
			return this;
		}
		
		/**
		 * Le constructeur
		 */
		public function LineTracer (pThickness:Number = 1, pColor:uint = 0xFF0000, pList:Array = null)
		{
			// Enregistrer les propriétés
			thickness = pThickness;
			color = pColor;
			
			__instances.push(this);
			
			// Et la liste s'il le faut
			if (pList != null)
				addPoints(pList);
		}
		
		/**
		 * Ajouter un point
		 */
		public function addPoint (pPoint:Object):LineTracer
		{
			// Vérifier si on trouve les coordonnées du point
			if (pPoint.x != null && pPoint.y != null)
				_points.push(new Point(pPoint.x, pPoint.y));
			
			// Retourner this
			return this;
		}
		
		/*
		 * Ajouter plusieurs points
		 */
		public function addPoints (pList:Array):LineTracer
		{
			// Ajouter tous les points
			for each (var item:Object in pList) 
			{
				addPoint(item);
			}
			
			// Retourner this
			return this;
		}
		
		
		/**
		 * Démarrer l'animation
		 */
		public function start ():LineTracer
		{
			// Séléctionner le premier index
			selectPoint(0);
			
			// Activer la boucle
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			
			// Retourner this
			return this;
		}
		
		/**
		 * Passer en mode de déplacement de sprite
		 */
		public function sparkle (pSparkleSprite:Sprite = null):LineTracer
		{
			// Définir le mode de dessin
			_sparkleMode  = true;
			
			// Vérifier si le sparkle à été passé en paramètres
			if (pSparkleSprite != null)
				setSparkleSprite(pSparkleSprite);
			
			// Retourner this
			return this;
		}
		
		/**
		 * Passer en mode line
		 */
		public function line ():LineTracer
		{
			// Définir le mode de dessin
			_sparkleMode  = false;
			
			// Retourner this
			return this;
		}
		
		/**
		 * Arrêter l'animation
		 */
		public function stop ():LineTracer
		{
			// Arrêter la boucle
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
			
			// Retourner this
			return this;
		}
		
		/**
		 * Effacer
		 */
		public function clear ():LineTracer
		{
			// Effacer le graphique
			graphics.clear();
			
			// Retourner this
			return this;
		}
		
		/**
		 * Ajouter du delais après le next
		 * @param	pDelay
		 * @return
		 */
		public function delay (pDelay:Number):LineTracer
		{
			// Enregistrer le delais
			_delay = pDelay;
			
			// Retourner this
			return this;
		}
		
		/**
		 * Effacer le sparkle
		 */
		public function removeSparkle ():LineTracer
		{
			// S'il existe on le kick
			if (_sparkleSprite != null)
			{
				removeChild(_sparkleSprite);
				_sparkleSprite = null;
			}
			
			// Retourner this
			return this;
		}
		
		/**
		 * Séléctionner un point
		 */
		public function selectPoint (pPointIndex:uint = 0):LineTracer
		{
			// Se souvenir de l'index
			_selectedPointIndex = pPointIndex;
			
			// Séléctionner le point courrant
			_currentPoint = _points[_selectedPointIndex];
			
			// Et le prochain point
			_nextPoint = _points[_selectedPointIndex + 1];
			
			// Déplacer
			if (_sparkleMode)
			{
				_sparkleSprite.x = _currentPoint.x;
				_sparkleSprite.y = _currentPoint.y;
			}
			else if (_selectedPointIndex == 0)
				graphics.moveTo(_currentPoint.x, _currentPoint.y);
			else
				graphics.lineTo(_currentPoint.x, _currentPoint.y);
		
			// On commence de 0
			_currentPosition = 0;
			
			// Retourner this
			return this;
		}
		
		/**
		 * Aller au point suivant
		 */
		public function nextPoint ():LineTracer
		{
			// Vérifier s'il y a encore un point
			if (_points[_selectedPointIndex + 2] == null)
			{
				// tracer le dernier bout de trait
				if (_sparkleMode)
				{
					_sparkleSprite.x = _nextPoint.x;
					_sparkleSprite.y = _nextPoint.y;
				}
				else
					graphics.lineTo(_nextPoint.x, _nextPoint.y);
				
				// C'est la fin
				endHandler();
			}
			else
				// Passer au point suivant
				selectPoint(_selectedPointIndex + 1);
			
			// Retourner this
			return this;
		}
		
		/**
		 * La fin
		 */
		private function endHandler ():void
		{
			// On stop
			stop();
			
			// Séléctionner la dernière fonction à appeler
			var endFunction:Function = _endFunctions.shift();
			
			// Si cette fonction existe
			if (endFunction != null)
			{
				// Vérifier si on a un délais
				if (_delay <= 0)
				{
					// L'appeler en passant this en paramètre
					endFunction(this);
				}
				else
				{
					// Appeler avec le délais
					setTimeout(endFunction, _delay * 1000, this);
					
					// On remet le délais à 0
					_delay = 0;
				}
			}
		}
		
		
		/**
		 * Remettre l'animation à 0
		 */
		public function reset ():LineTracer
		{
			// On retourne à 0
			selectPoint(0);
			
			// Retourner this
			return this;
		}
		
		/**
		 * La boucle
		 * @param	event
		 */
		private function enterFrameHandler (event:Event):void
		{
			// Calculer la nouvelle position
			_currentPosition += _speed / 100;
			
			// Vérifier
			if (_currentPosition <= 1)
			{
				// Calculer la position
				var xx:Number = _currentPoint.x - _currentPosition * (_currentPoint.x - _nextPoint.x);
				var yy:Number = _currentPoint.y - _currentPosition * (_currentPoint.y - _nextPoint.y);
				
				// Dessiner
				if (_sparkleMode)
				{
					_sparkleSprite.x = xx;
					_sparkleSprite.y = yy;
				}
				else
					graphics.lineTo(xx, yy);
			}
			else
			{
				nextPoint();
			}
		}
		
		/**
		 * Appelée après le traçage
		 */
		public function next (pFunction:Function):LineTracer
		{
			// Ajouter à la liste des fonctions
			_endFunctions.push(pFunction);
			
			// Retourer this
			return this;
		}
		
		/**
		 * Tuer
		 */
		public function kill ():void
		{
			removeSparkle();
			clear();
		}
	}
}